

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

// https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x804 上介绍了“空白页”项模板


using Windows.Devices.Bluetooth;
using Windows.Devices.Bluetooth.GenericAttributeProfile;
using Windows.Devices.Enumeration;
using Windows.UI.Core;
using System.Text;
using Windows.Security.Cryptography;
using Windows.Storage.Streams;
using Windows.Devices.Bluetooth.Advertisement;

namespace App2
{
    public class DBG_GOBAL
    {
        public static TextBox log_textbox;
        public static int lines = 0;

        public async static void WriteLine(String msg) 
        {
            await log_textbox.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                if (lines > 80)
                {
                    // log_textbox.Text = "";
                    try
                    {
                        log_textbox.Text.Remove(0, log_textbox.Text.IndexOf("\n"));
                    }
                    catch
                    {

                    }
                    
                    // lines = 0;
                }else
                {
                    lines += 1;
                }
                log_textbox.Text += msg + "\n";
                var grid = (Grid)VisualTreeHelper.GetChild(log_textbox, 0);
                for (var i = 0; i <= VisualTreeHelper.GetChildrenCount(grid) - 1; i++)
                {
                    object obj = VisualTreeHelper.GetChild(grid, i);
                    if (!(obj is ScrollViewer)) continue;
                    ((ScrollViewer)obj).ChangeView(0.0f, ((ScrollViewer)obj).ExtentHeight, 1.0f);
                    break;
                }
            });
           //await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
           // {
                //System.Diagnostics.Debug.WriteLine(msg);
            //});

        }
    }

	public class NutCharaceristic
	{
		private DeviceInformation deviceInfor = null;

		public NutCharaceristic(DeviceInformation deviceInfor)
		{
			this.deviceInfor = deviceInfor;
		}
	}

    public class NutGuid {
        public const String SERVICE_UUID = "0abc1200-3935-11e5-85a6-0002a5d5c50b";
        public const String POWER_CHAR_UUID = "0abc1201-3935-11e5-85a6-0002a5d5c51b";
        public const String ACCELERATE_CHAR_UUID = "0abc1201-3935-11e5-85a6-0002a5d5c52b";
        public const String HEARTRATE_CHAR_UUID = "0abc1201-3935-11e5-85a6-0002a5d5c53b";
        public const String ECG_CHAR_UUID = "0abc1201-3935-11e5-85a6-0002a5d5c54b";
        public const String CMD_CHAR_UUID = "0abc1201-3935-11e5-85a6-0002a5d5c55b";
    }

	public class NutBle
	{

        public MainPage mainPage;
        

        private DeviceWatcher deviceWatcher = null;
		private BluetoothLEDevice bluetoothLeDevice = null;
		//private DeviceInformation deviceInformation = null;
		private DevicePairingResult result = null;
		private GattCharacteristic csc_characteristic = null;
        private DeviceInformation sel_devices = null;

        private GattCharacteristic cmd_characteristic = null;

        private bool pair_status = false;

        private List<DeviceInformation> devices;
        private GattDeviceServicesResult service_result;

        

        public NutBle()
        {
            devices = new List<DeviceInformation>();
        }

        private int seed = 0;
        private List<Report> lstSource = new List<Report>();
        private int xx_i = 0;
        public void update_data_line(byte[] data)
        {

            mainPage.update_source(data);

            //Random random = new Random(seed);
            ////DBG_GOBAL.WriteLine("update data line");
            //if (lstSource.Count > 21)
            //{
            //    //lstSource.RemoveAt(0);
            //    lstSource.RemoveRange(0, 3);
            //}

            //for(var i =0; i < data.Count(); i++)
            //{
            //    xx_i += 1;
            //    //lstSource.Add(new Report() { months = xx_i.ToString(), countlent = BitConverter.ToInt32(data, 0) });
            //    lstSource.Add(new Report() { months = xx_i.ToString(), countlent = (int)data[i] });
            //}
            ////List<Report> lstSource = new List<Report>();
            ////xx_i += 1;
            ////for(var i =1; i<30; i++)
            ////{
            ////    seed += i;
            //seed += xx_i;
            // //lstSource.Add(new Report() { months =  xx_i.ToString() , countlent = random.Next() % 50 });
            ////    break;
            ////}

            ////lstSource.Add(new Report() { months = "1", countlent = 30 });
            ////lstSource.Add(new Report() { months = "2", countlent = 15 });
            ////lstSource.Add(new Report() { months = "3", countlent = 20 });
            ////lstSource.Add(new Report() { months = "4", countlent = 10 });
            ////lstSource.Add(new Report() { months = "5", countlent = 13 });
            ////lstSource.Add(new Report() { months = "6", countlent = 1 });
            ////lstSource.Add(new Report() { months = "7", countlent = 33 });
            ////lstSource.Add(new Report() { months = "8", countlent = 41 });
            ////lstSource.Add(new Report() { months = "9", countlent = 31 });
            ////lstSource.Add(new Report() { months = "10", countlent = 21 });
            ////lstSource.Add(new Report() { months = "11", countlent = 12 });
            ////lstSource.Add(new Report() { months = "12", countlent = 37 });

            //mainPage.update_source(lstSource.ToList());
        }

        private void dev_watcher()
        {
            devices.Clear();

            // Additional properties we would like about the device.
            string[] requestedProperties = { "System.Devices.Aep.DeviceAddress", "System.Devices.Aep.IsConnected" };

            // BT_Code: Currently Bluetooth APIs don't provide a selector to get ALL devices that are both paired and non-paired.
            deviceWatcher =
                    DeviceInformation.CreateWatcher(
                        "(System.Devices.Aep.ProtocolId:=\"{bb7bb05e-5972-42b5-94fc-76eaa7084d49}\")",
                        requestedProperties,
                        DeviceInformationKind.AssociationEndpoint);

            // Register event handlers before starting the watcher.
            deviceWatcher.Added += DeviceWatcher_Added;
            deviceWatcher.Updated += DeviceWatcher_Updated;
            deviceWatcher.Removed += DeviceWatcher_Removed;
            deviceWatcher.EnumerationCompleted += DeviceWatcher_EnumerationCompletedAsync;
            deviceWatcher.Stopped += DeviceWatcher_Stopped;

            // Start the watcher.
            deviceWatcher.Start();
            
        }

        

        private async void DeviceWatcher_Added(DeviceWatcher sender, DeviceInformation deviceInfo)
        {
            if (sender == deviceWatcher)
            {
                devices.Add(deviceInfo);
                DBG_GOBAL.WriteLine(deviceInfo.Name + deviceInfo.Id);
            }
        }

        private async void DeviceWatcher_Updated(DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate)
        {
        }

        private async void DeviceWatcher_Removed(DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate)
        {
        }

        private async void DeviceWatcher_EnumerationCompletedAsync(DeviceWatcher sender, object e)
        {
            DBG_GOBAL.WriteLine("enumeration complete call");
        }

        private async void DeviceWatcher_Stopped(DeviceWatcher sender, object e)
        {
            DBG_GOBAL.WriteLine("watcher stopped call");
        }

		public void scan_dev()
		{
            stop_scan_dev();
            dev_watcher();
        }

        public void stop_scan_dev()
        {
            if (deviceWatcher == null) return;

            DBG_GOBAL.WriteLine("watcher status: " + deviceWatcher.Status.ToString());
            if ( deviceWatcher.Status == DeviceWatcherStatus.Started)
            {
                deviceWatcher.Stop();
            }
        }


        private async void PairingRequestedHandler(
            DeviceInformationCustomPairing sender,
            DevicePairingRequestedEventArgs args)
        {
            DBG_GOBAL.WriteLine("pariling");
            switch(args.PairingKind) {
                case DevicePairingKinds.ProvidePin:
                        args.Accept("123456");
                        break;
                default:
                    DBG_GOBAL.WriteLine("no paril type/kind auth");
                    break;
            }
        }

        public async void pair_dev()
		{

            if (sel_devices == null) {
                DBG_GOBAL.WriteLine("please selceto a device before pair");
                return;
            }

            var deviceInformation = sel_devices;

            if (/*deviceInformation.Pairing.IsPaired ||*/ pair_status) {
                DBG_GOBAL.WriteLine("sel device already paired");
                return;
            }

            DBG_GOBAL.WriteLine("Pairing.CanPair: " + deviceInformation.Pairing.CanPair.ToString());
            DBG_GOBAL.WriteLine("Pairing.IsPaired: " + deviceInformation.Pairing.IsPaired.ToString());
            var custom_p = deviceInformation.Pairing.Custom;


            System.Diagnostics.Debug.WriteLine("this is test hello\n");
            custom_p.PairingRequested += PairingRequestedHandler;
            //deviceInformation.Kind.p
            //DevicePairingKinds pkind =  ;
            //DevicePairingResult result = await custom_p.PairAsync(DevicePairingKinds.DisplayPin | DevicePairingKinds.ConfirmPinMatch);
            result = await custom_p.PairAsync(DevicePairingKinds.ProvidePin, DevicePairingProtectionLevel.None);
            custom_p.PairingRequested -= PairingRequestedHandler;
            
            //deviceInformation.Pairing.PairAsync();
            DBG_GOBAL.WriteLine(result.Status.ToString());

            if (result.Status == DevicePairingResultStatus.Paired || result.Status == DevicePairingResultStatus.AlreadyPaired)
            {
                //await deviceInformation.Pairing.UnpairAsync();
                pair_status = true;
            }
        }

        public async void clear_conn()
        {
            if (bluetoothLeDevice != null) {
                bluetoothLeDevice = null;
            }

            if (sel_devices != null) {
                sel_devices = null;
            }

            pair_status = false;
        }

		public async void unpair_dev()
		{
            if (sel_devices == null){
                DBG_GOBAL.WriteLine("please sel a devices use [sel n] before unpair\n");
                return;
            }
            var deviceInformation = sel_devices;
            if (deviceInformation.Pairing.IsPaired) { 
               await deviceInformation.Pairing.UnpairAsync();
               DBG_GOBAL.WriteLine("sel devices unpaired");
            }
            else
            {
                DBG_GOBAL.WriteLine("sel devices not paried");
            }

            clear_conn();
		}

		public void disconn_dev()
		{

		}

        public async void status_dev()
        {
            if (sel_devices == null) {
                DBG_GOBAL.WriteLine("not select device");
                return;
            }

            DBG_GOBAL.WriteLine("pair_status " + pair_status);
            DBG_GOBAL.WriteLine("sel_devices.Pairing.IsPaired " + sel_devices.Pairing.IsPaired);
            DBG_GOBAL.WriteLine("services: ");
            DBG_GOBAL.WriteLine("characteristics: ");            
        }

		public async void sel_dev(int index)
		{
            if (index < devices.Count && index >= 0)
            {
                if (sel_devices == null)
                {
                    sel_devices = devices[index];
                    pair_status = sel_devices.Pairing.IsPaired;
                    DBG_GOBAL.WriteLine("success sel devices index: " + index.ToString());
                }else
                {
                    DBG_GOBAL.WriteLine("have sel dev");
                }
            }
            else
            {
                DBG_GOBAL.WriteLine("over index: " + index.ToString());
            }
		}

        public async void conn_dev(){
            if (sel_devices == null) {
                DBG_GOBAL.WriteLine("pelease use [sel n] sel a device before connect");
                return;
            }

            if (/*sel_devices.Pairing.IsPaired == false || */ ! pair_status)
            {
                DBG_GOBAL.WriteLine("pelase use [pair] cmd before connect");
                return;
            }

            if (bluetoothLeDevice == null)
            {
                var ble_id = sel_devices.Id;
                bluetoothLeDevice = await BluetoothLEDevice.FromIdAsync(ble_id);
                if (bluetoothLeDevice != null) {
                    DBG_GOBAL.WriteLine("connect success: " + ble_id);
                    list_service();
                }else {
                    DBG_GOBAL.WriteLine("connect failed: " + ble_id);
                }
            }else{
                DBG_GOBAL.WriteLine("already connect");
            }
        }

        public async void sel_service(int n)
        {
            if (bluetoothLeDevice == null)
            {
                DBG_GOBAL.WriteLine("please conn first");
                return;
            }
            Guid uuid = new Guid("0abc1200-3935-11e5-85a6-0002a5d5c50b");
            // Guid uuid = new Guid("fd5abba0-3935-11e5-85a6-0002a5d5c53b");
            // GattDeviceService service = bluetoothLeDevice.GetGattService(uuid);
            service_result = await bluetoothLeDevice.GetGattServicesForUuidAsync(uuid);
            if (service_result == null)
            {
                DBG_GOBAL.WriteLine("not found target service uuid, uuid list:");
                list_service();
            }else{
                DBG_GOBAL.WriteLine("select service uuid: " + uuid.ToString());
            }
        }

        public async void init_chars()
        {
            init_or_show_char(false);
        }

        public async void init_or_show_char(bool only_show)
        {
            if (service_result == null)
            {
                DBG_GOBAL.WriteLine("please sel a service before characteristics op");
                list_service();
            }
            else
            {
                list_service();
                DBG_GOBAL.WriteLine("found nut ble servie, count:" + service_result.Services.Count.ToString());

                foreach (var service in service_result.Services)
                {
                    GattCharacteristicsResult characteristics = null;
                    // IReadOnlyList < GattCharacteristic > characteristicss = null;
                    // var characteristics = service.GetAllCharacteristics();
                    try
                    {
                        // BT_Code: Get all the child characteristics of a service.
                        // characteristicss = service.GetAllCharacteristics();
                        characteristics = await service.GetCharacteristicsAsync();
                    }
                    catch (Exception ex)
                    {
                        //rootPage.NotifyUser("Restricted service. Can't read characteristics: " + ex.Message,
                        //    NotifyType.ErrorMessage);
                        // On error, act as if there are no characteristics.
                        //characteristics = new List<GattCharacteristic>();
                        //testbox1.Text += "error: " + ex.Message + "\n";
                        DBG_GOBAL.WriteLine("list characteristic exception");
                    }
                    
                    // DBG_GOBAL.WriteLine("serv char count: " + characteristics.Count.ToString());

                    // foreach (var charact in characteristics)
                    // {
                    //     reg_characteristic(charact);
                    // }

                    // DBG_GOBAL.WriteLine("serv char count: " + characteristics.Characteristics.Count.ToString());

                    // if(characteristicss == null) {
                    //     DBG_GOBAL.WriteLine("null regggggggggggggggg");
                    //     return;
                    // }
                    // DBG_GOBAL.WriteLine("serv char count: " + characteristicss.Count.ToString());
                    // foreach (var charact in characteristicss)
                    foreach (var charact in characteristics.Characteristics)
                    {
                        if (only_show) {
                            show_charteritic(charact);
                        }else{
                            reg_characteristic(charact);
                        }
                    }
                }
            }
            //foreach (var service in bluetoothLeDevice.GattServices)
        }

        // private async void register_val_cb()
        //     {
        //         var result = await
        //                 csc_characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(
        //                     GattClientCharacteristicConfigurationDescriptorValue.Notify);
        //         if (result == GattCommunicationStatus.Success)
        //         {
        //             //if (!isValueChangedHandlerRegistered)
        //             //{
        //                 csc_characteristic.ValueChanged += Characteristic_ValueChanged;
        //                 //isValueChangedHandlerRegistered = true;
        //             //}

        //         }

        //     }
        GattCharacteristic xx_characteristic;

        public async void test_read_value(GattCharacteristic characteristic)
        {
            if (characteristic == null) return;
            try
            {
                var rs = await characteristic.ReadValueAsync();
                var ibuf = rs.Value;
                if (ibuf == null)
                {
                    //DBG_GOBAL.WriteLine("null====");
                }
                else
                {
                    var o = ibuf.ToString();
                    DBG_GOBAL.WriteLine(o);
                }
            }
            catch
            {
                DBG_GOBAL.WriteLine("ble read exception");
            }
        }

         public async void read_value()
        {
            test_read_value(xx_characteristic);
            
        }
        private async void register_read(GattCharacteristic characteristic)
        {
            try
            {
                var result = await characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(
                GattClientCharacteristicConfigurationDescriptorValue.Notify);
                if (result == GattCommunicationStatus.Success)
                {
                    characteristic.ValueChanged += Characteristic_ValueChanged;
                    DBG_GOBAL.WriteLine("register read success");
                }
                else
                {
                    DBG_GOBAL.WriteLine("register read call faild: " + result.ToString());
                }
            }
            catch{
                DBG_GOBAL.WriteLine("notify error");
            }
        }

        private async void reg_characteristic(GattCharacteristic characteristic)
        {
            //SensorUUIDs.UUID_ENV_SERV
                       
            if (characteristic == null)
            {
                DBG_GOBAL.WriteLine("warning reg null characteristic");
                return;
            }
            // characteristic.ValueChanged += Characteristic_ValueChanged;
            register_read(characteristic);
                
            DBG_GOBAL.WriteLine("reg_characteristic uuid: " + characteristic.Uuid.ToString());

            switch (characteristic.Uuid.ToString())
            {   
                //case "fd5abba1-3935-11e5-85a6-0002a5d5c51b":
                case NutGuid.CMD_CHAR_UUID:
                    cmd_characteristic = characteristic;
                    DBG_GOBAL.WriteLine("reg service char");
                    break;

                case NutGuid.POWER_CHAR_UUID:
                    DBG_GOBAL.WriteLine("reg power_char char");
                    break;

                case NutGuid.ACCELERATE_CHAR_UUID:
                    DBG_GOBAL.WriteLine("reg accelerate_char char");
                    break;

                case NutGuid.HEARTRATE_CHAR_UUID:
                    //xx_characteristic = characteristic
                    DBG_GOBAL.WriteLine("reg heartrate_char char");
                    break;

                case NutGuid.ECG_CHAR_UUID:
                    xx_characteristic = characteristic;
                    DBG_GOBAL.WriteLine("reg ecg_char char");
                    break;

                default:
                    DBG_GOBAL.WriteLine("default char , no pre match");
                    break;
            }
        }

		public void list_service()
		{
            if (bluetoothLeDevice == null) return;
            DBG_GOBAL.WriteLine("===========list services===========");
            foreach (var servie in bluetoothLeDevice.GattServices)
            {
                DBG_GOBAL.WriteLine("service uuid: " + servie.Uuid.ToString());
            }
            DBG_GOBAL.WriteLine("====================================");
		}

		public void list_service_characteristic()
		{
            init_or_show_char(true);
		}

        private void show_charteritic(GattCharacteristic charact)
        {
            DBG_GOBAL.WriteLine(charact.Uuid.ToString());
        }

        /*=================char hanble===============*/
        public async void send_cmd(String cmd)
        {
            if (cmd_characteristic == null)
            {
                DBG_GOBAL.WriteLine("please before register service and callback");
                return;
            }
            var writer = new DataWriter();
            writer.WriteString(cmd);
            GattCommunicationStatus result = await cmd_characteristic.WriteValueAsync(writer.DetachBuffer());
            if (result == GattCommunicationStatus.Success)
            {
                DBG_GOBAL.WriteLine("sucess cmd; " + cmd);
            }
            else
            {
                DBG_GOBAL.WriteLine(result.ToString() + " cmd:" + cmd);
            }
        }

        private void Characteristic_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args)
        {
            try
            {


                // BT_Code: An Indicate or Notify reported that the value has changed.
                // Display the new value with a timestamp.
                //var newValue = FormatValueByPresentation(args.CharacteristicValue, presentationFormat);
                //DBG_GOBAL.WriteLine("dddddddddddddddddddddddddddddddddddd");
                //test_read_value(sender);

                switch (sender.Uuid.ToString())
                {

                    case NutGuid.POWER_CHAR_UUID:
                        DBG_GOBAL.WriteLine("recv power_char char");
                        break;

                    case NutGuid.ACCELERATE_CHAR_UUID:
                        DBG_GOBAL.WriteLine("recv accelerate_char char");
                        break;

                    case NutGuid.HEARTRATE_CHAR_UUID:
                        DBG_GOBAL.WriteLine("recv heartrate_char char");
                        break;

                    case NutGuid.ECG_CHAR_UUID:
                        {
                            //var newValuea = FormatValueByPresentation(sender, args.CharacteristicValue, null);

                            byte[] data;
                            CryptographicBuffer.CopyToByteArray(args.CharacteristicValue, out data);
                            test_read_value(sender);

                            //DBG_GOBAL.WriteLine("len: " + data.Count().ToString() + ", " + data[0].ToString());

                            update_data_line(data);
                            //test_read_value(sender);

                            return;
                        }
                        DBG_GOBAL.WriteLine("recv ecg_char char");
                        break;

                    case NutGuid.CMD_CHAR_UUID:
                        DBG_GOBAL.WriteLine("recv service char");
                        break;

                    default:
                        DBG_GOBAL.WriteLine("default char , no pre match");
                        break;
                }
                //update_data_line();
                var newValue = FormatValueByPresentation(sender, args.CharacteristicValue, null);
                var message = $"Value at {DateTime.Now:hh:mm:ss.FFF}: {newValue}";
                DBG_GOBAL.WriteLine("recv msg: " + message);
            }
            catch
            {
                DBG_GOBAL.WriteLine("ble exception");

            }
            test_read_value(sender);
        }

        
        private string FormatValueByPresentation(GattCharacteristic characteristic,
            IBuffer buffer, GattPresentationFormat format)
        {
            // BT_Code: For the purpose of this sample, this function converts only UInt32 and
            // UTF-8 buffers to readable text. It can be extended to support other formats if your app needs them.
            byte[] data;
            CryptographicBuffer.CopyToByteArray(buffer, out data);
            if (format != null)
            {
                if (format.FormatType == GattPresentationFormatTypes.UInt32 && data.Length >= 4)
                {
                    return BitConverter.ToInt32(data, 0).ToString();
                }
                else if (format.FormatType == GattPresentationFormatTypes.Utf8)
                {
                    try
                    {
                        return Encoding.UTF8.GetString(data);
                    }
                    catch (ArgumentException)
                    {
                        return "(error: Invalid UTF-8 string)";
                    }
                }
                else
                {
                    // Add support for other format types as needed.
                    return "Unsupported format: " + CryptographicBuffer.EncodeToHexString(buffer);
                }
            }
            else
            {
                // We don't know what format to use. Let's try a well-known profile, or default back to UTF-8.
                if (characteristic.Uuid.Equals(GattCharacteristicUuids.HeartRateMeasurement))
                {
                    try
                    {
                        return "lll";
                        //return "Heart Rate: " + ParseHeartRateValue(data).ToString();
                    }
                    catch (ArgumentException)
                    {
                        return "Heart Rate: (unable to parse)";
                    }
                }
                else if (characteristic.Uuid.Equals(GattCharacteristicUuids.BatteryLevel))
                {
                    try
                    {
                        // battery level is encoded as a percentage value in the first byte according to
                        // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.battery_level.xml
                        return "Battery Level: " + data[0].ToString() + "%";
                    }
                    catch (ArgumentException)
                    {
                        return "Battery Level: (unable to parse)";
                    }
                }
                else
                {
                    try
                    {
                        return "Unknown format: " + Encoding.UTF8.GetString(data);
                    }
                    catch (ArgumentException)
                    {
                        return "Unknown format";
                    }
                }
            }
        }

	}


}